Skip to main content

API Gateway

api-gateway (VMID 117, 10.0.0.17) runs the public ABAIR API, served at api.abair.ie/v4/. The gateway checks API keys and credits, applies rate limits, then forwards each request to the right back-end service.

Playbookansible/hetzner-main/setup-api-gateway.yml
Settingsansible/hetzner-main/vars/api-gateway.yml
Stack directory/opt/api-gateway
Make targetssetup-api-gateway, update-api-gateway, enroll-wazuh-api-gateway
SecretsDocker Registry Password, Webserver htpasswd admin, and the Secure Note api-gateway .env.secrets

Containers​

The playbook writes a Docker Compose file that runs four containers. The images come from the private registry on docker-build.

ContainerImagePort on the VMPurpose
api-gatewayapi-gateway-production:main4004The gateway. Memory limit 2 GB. Writes logs to /opt/api-gateway/logs/.
api-authenticationapi-authentication:main4005Authentication service, called by the gateway.
api-gateway-docsapi-gateway-docs:main5100The API documentation site (Next.js).
redisredis:alpinenot publishedCache and rate-limit state for the gateway.

The webserver routes api.abair.ie like this:

PathGoes to
/v4/api-gateway (4004). /metrics and apikey/revoke are blocked from outside.
/v4/recognition/streamingapi-gateway (4004), as a WebSocket
/v4/docs and /api-gateway-docs (5100)
/v3/the legacy v3 API (abair-api) on services-user

Back-end services​

The gateway reaches its back ends directly over the private networks, never through the webserver. The addresses are set in vars/api-gateway.yml and written to /opt/api-gateway/.env.

SettingAddressService
SYNTHESIS192.168.100.2:8004Speech synthesis on Amergin
RECOGNITION_BATCH192.168.100.2:8002Batch speech recognition on Amergin
RECOGNITION_STREAMING192.168.100.2:8080Streaming speech recognition (Triton) on Amergin
SUBTITLES192.168.100.2:4040Fotheidil API on Amergin
PREDICTIVE_TEXT192.168.100.2:8003Predictive text on Amergin
POS_TAGGER10.0.0.19:6789irishfst on services-staff
BUNAMO10.0.0.19:9000bunamo on services-staff
GRAMSRV10.0.0.19:10001gramsrv on services-staff
GRAMADOIR10.0.0.19:10002gramadoir on services-staff
AUTHapi-authentication:4005The authentication container
SUPABASE_URLSupabase (hosted)Database and user accounts

The same file also sets the rate limiter (100 requests per minute), the daily credit limit for anonymous IPs (3840), and a circuit breaker, which is turned off.

Amergin and services-staff both allow 10.0.0.17 through their firewalls for these ports.

Secrets​

Secrets live in /opt/api-gateway/.env.secrets. The playbook fetches that file from the Vaultwarden Secure Note api-gateway .env.secrets, using your unlocked bw session, and writes it with owner root:deploy, mode 0640. If the note is missing, or you don't have access to it, the playbook stops.

Deployments​

  • Code changes are deployed by the gateway's own CI. The pipeline builds the image on the GitHub runner on docker-build, pushes it to the registry, then connects as the deploy user and runs docker compose pull api-gateway && docker compose up -d api-gateway. The playbook authorizes the runner's SSH key for that user.
  • Configuration changes (anything in vars/api-gateway.yml or the compose file) need make update-api-gateway. That pulls every image and recreates changed containers.

Firewall​

UFW allows SSH from anywhere, and ports 4004 and 4005 from the webserver only. The UFW rules are applied by the setup tag only, so update-api-gateway doesn't change them.

note

There is no UFW rule for port 5100, yet the docs site works through the webserver. That's because ports published by Docker bypass UFW's input rules. The same applies to 4004 and 4005: UFW is not what actually restricts them. See Networking → Firewalls.

Other details​

  • The VM has a 2 GB swap file. Its memory was raised from 2 GB to 4 GB after an out-of-memory incident on 2026-08-11, when a burst of subtitle uploads got the gateway process killed.
  • Alloy ships the gateway's log file and scrapes /metrics from both api-gateway and api-authentication.